/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include "local-include/reg.h"
#include <cpu/cpu.h>
#include <cpu/ifetch.h>
#include <cpu/decode.h>
#include <trace.h>

#define R(i) gpr(i)
#define Mr vaddr_read
#define Mw vaddr_write

word_t RCSR(int csr_num){
  switch (csr_num){
    case 0x305: return cpu.mtvec;
    case 0x341: return cpu.mepc;
    case 0x342: return cpu.mcause;
    case 0x300: return cpu.mstatus;
    default: Assert(0, "read csr_num = %x", csr_num);
  }
  return 0;
}

void WCSR(int csr_num, uint64_t wdata){
  switch (csr_num){
    case 0x305: cpu.mtvec = wdata; break;
    case 0x341: cpu.mepc = wdata; break;
    case 0x342: cpu.mcause = wdata; break;
    case 0x300: cpu.mstatus = wdata; break;
    default: Assert(0, "write csr_num = %x", csr_num);
  }
}

void down_ftrace(uint64_t spc, uint64_t snpc, int call_ret){
  #ifdef CONFIG_FTRACE
    extern _ELF_function ELF_function[MAX_FUC_NUM];
    extern int ELF_function_num;
    extern char *ftrace_file;
    FILE *fp = fopen(ftrace_file, "a");
    int i;
    for(i = 0; i < ELF_function_num; i++){
      if(snpc == ELF_function[i].addr && call_ret == 1){
        fprintf(fp, "%8x: call %s[%8x]\n", (uint32_t)spc, ELF_function[i].name, (uint32_t)ELF_function[i].addr);
        fclose(fp);
        return;
      }
      else if(snpc >= ELF_function[i].addr && snpc < (ELF_function[i].addr + ELF_function[i].size) && call_ret == 0){
        fprintf(fp, "%8x: ret %s[%8x]\n", (uint32_t)spc, ELF_function[i].name, (uint32_t)ELF_function[i].addr);
        fclose(fp);
        return;
      }
    }
    fclose(fp);
  #endif
}

enum {
  TYPE_I, TYPE_U, TYPE_S,
  TYPE_N, TYPE_J, TYPE_R, TYPE_B  // none
};

#define src1R(n) do { *src1 = R(n); } while (0)
#define src2R(n) do { *src2 = R(n); } while (0)
#define destR(n) do { *dest = n; } while (0)
#define src1I(i) do { *src1 = i; } while (0)
#define src2I(i) do { *src2 = i; } while (0)
#define destI(i) do { *dest = i; } while (0)

static word_t immI(uint32_t i) { return SEXT(BITS(i, 31, 20), 12); }
static word_t immU(uint32_t i) { return SEXT(BITS(i, 31, 12), 20) << 12; }
static word_t immS(uint32_t i) { return SEXT(BITS(i, 31, 25), 7) << 5 | BITS(i, 11, 7); }
static word_t immJ(uint32_t i) { return SEXT(BITS(i, 31, 31), 1) << 20 | BITS(i, 19, 12) << 12 | BITS(i, 20, 20) << 11 | BITS(i, 30, 21) << 1;}
static word_t immB(uint32_t i) { return SEXT(BITS(i, 31, 31), 1) << 12 | BITS(i, 7, 7) << 11 | BITS(i, 30, 25) << 5 | BITS(i, 11, 8) << 1;}

int rd, rs1, rs2;
static void decode_operand(Decode *s, word_t *dest, word_t *src1, word_t *src2, int type) {
  uint32_t i = s->isa.inst.val;
  rd  = BITS(i, 11, 7);
  rs1 = BITS(i, 19, 15);
  rs2 = BITS(i, 24, 20);
  destR(rd);
  switch (type) {
    case TYPE_I: src1R(rs1); src2I(immI(i)); break;
    case TYPE_U: src1I(immU(i)); break;
    case TYPE_S: destI(immS(i)); src1R(rs1); src2R(rs2); break;
    case TYPE_J: src1I(immJ(i)); break;
    case TYPE_R: src1R(rs1); src2R(rs2); break;
    case TYPE_B: destI(immB(i)); src1R(rs1); src2R(rs2); break;
  }
}

static int decode_exec(Decode *s) {
  word_t dest = 0, src1 = 0, src2 = 0, csr = 0;
  s->dnpc = s->snpc;

#define INSTPAT_INST(s) ((s)->isa.inst.val)
#define INSTPAT_MATCH(s, name, type, ... /* body */ ) { \
  decode_operand(s, &dest, &src1, &src2, concat(TYPE_, type)); \
  __VA_ARGS__ ; \
}

  INSTPAT_START();                   
  INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc  , U, R(dest) = src1 + s->pc);
  INSTPAT("??????? ????? ????? 011 ????? 00000 11", ld     , I, R(dest) = Mr(src1 + src2, 8));
  INSTPAT("??????? ????? ????? 011 ????? 01000 11", sd     , S, Mw(src1 + dest, 8, src2));

  INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); 
  INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui    , U, R(dest) = src1);
  INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi   , I, R(dest) = src1 + src2);

  INSTPAT("000000? ????? ????? 001 ????? 00100 11", slli   , I, R(dest) = src1 << src2);
  INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal    , J, R(dest) = s->pc + 4; s->dnpc = s->pc + src1; if(rd == 1){down_ftrace(s->pc, s->dnpc, 1);});
  INSTPAT("??????? ????? ????? 000 ????? 11001 11", jalr   , I, s->dnpc = (src1 + src2) & 0xfffffffffffffffe; R(dest) = s->pc + 4; if(rd == 0 && rs1 == 1){down_ftrace(s->pc, s->dnpc, 0);});

  INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw     , I, R(dest) = SEXT(Mr((src1 + src2), 4), 32));
  INSTPAT("0000000 ????? ????? 000 ????? 01110 11", addw   , R, R(dest) = SEXT((uint32_t)(src1 + src2), 32));
  INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub    , R, R(dest) = src1 - src2);

  INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu  , I, R(dest) = src1 < src2);
  INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq    , B, if(src1 == src2){s->dnpc = s->pc + dest;});
  INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne    , B, if(src1 != src2){s->dnpc = s->pc + dest;});

  INSTPAT("??????? ????? ????? 000 ????? 00110 11", addiw  , I, R(dest) = SEXT((uint32_t)(src1 + src2), 32));
  INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add    , R, R(dest) = src1 + src2);
  INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh     , S, Mw(src1 + dest, 2, (uint16_t)src2));

  INSTPAT("010000? ????? ????? 101 ????? 00100 11", srai   , I, R(dest) = src1 >> 63 == 1 ? src1 >> src2 | 0xffffffffffffffff << (64 - src2) : src1 >> src2);
  INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu    , I, R(dest) = (uint64_t)Mr(src1 + src2, 1));
  INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi   , I, R(dest) = src1 & src2);

  INSTPAT("0000000 ????? ????? 001 ????? 01110 11", sllw   , R, R(dest) = SEXT((uint32_t)src1 << BITS(src2, 4, 0), 32));
  INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and    , R, R(dest) = src1 & src2);
  INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu   , R, R(dest) = src1 < src2);

  INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori   , I, R(dest) = src1 ^ src2);
  INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or     , R, R(dest) = src1 | src2);
  INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb     , S, Mw(src1 + dest, 1, (uint8_t)src2));
                
  INSTPAT("000000? ????? ????? 101 ????? 00100 11", srli   , I, R(dest) = src1 >> src2);
  INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge    , B, if((int64_t)src1 >= (int64_t)src2){s->dnpc = s->pc + dest;});
  INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw     , S, Mw(src1 + dest, 4, (uint32_t)src2));

  INSTPAT("0000001 ????? ????? 000 ????? 01110 11", mulw   , R, R(dest) = SEXT((uint32_t)(src1 * src2), 32));
  INSTPAT("0000001 ????? ????? 100 ????? 01110 11", divw   , R, R(dest) = SEXT((int32_t)src1 / (int32_t)src2, 32));
  INSTPAT("0000001 ????? ????? 110 ????? 01110 11", remw   , R, R(dest) = SEXT((int32_t)src1 % (int32_t)src2, 32));

  INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt    , B, if((int64_t)src1 < (int64_t)src2){s->dnpc = s->pc + dest;});
  INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt    , R, R(dest) = (int64_t)src1 < (int64_t)src2);
  INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh     , I, R(dest) = SEXT(Mr(src1 + src2, 2), 16));

  INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu    , I, R(dest) = (uint64_t)Mr(src1 + src2, 2));
  INSTPAT("0100000 ????? ????? 000 ????? 01110 11", subw   , R, R(dest) = SEXT((uint32_t)(src1 - src2), 32));
  INSTPAT("000000? ????? ????? 001 ????? 00110 11", slliw  , I, R(dest) = SEXT((uint32_t)(src1 << src2), 32));

  INSTPAT("010000? ????? ????? 101 ????? 00110 11", sraiw  , I, if(BITS(src2, 5, 5) == 0){R(dest) = SEXT(((signed)BITS(src1, 31, 0) >> BITS(src2, 5, 0)), 32);});
  INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul    , R, R(dest) = src1 * src2);
  INSTPAT("000000? ????? ????? 101 ????? 00110 11", srliw  , I, if(BITS(src2, 5, 5) == 0){R(dest) = SEXT((BITS(src1, 31, 0) >> BITS(src2, 5, 0)), 32);});

  INSTPAT("0000000 ????? ????? 101 ????? 01110 11", srlw   , R, R(dest) = SEXT((BITS(src1, 31, 0) >> BITS(src2, 4, 0)), 32));
  INSTPAT("0100000 ????? ????? 101 ????? 01110 11", sraw   , R, R(dest) = SEXT((signed)BITS(src1, 31, 0) >> BITS(src2, 4, 0), 32));
  INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu   , B, if(src1 < src2){s->dnpc = s->pc + dest;});
  
  INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu   , B, if(src1 >= src2){s->dnpc = s->pc + dest;});
  INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu   , R, R(dest) = src1 / src2);
  INSTPAT("??????? ????? ????? 010 ????? 00100 11", slti   , I, if((int64_t)src1 < (int64_t)src2){R(dest) = 1;} else{R(dest) = 0;});

  INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor    , R, R(dest) = src1 ^ src2);
  INSTPAT("0000001 ????? ????? 111 ????? 01110 11", remuw  , R, R(dest) = SEXT(BITS(src1, 31, 0) % BITS(src2, 31, 0), 32));
  INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll    , R, R(dest) = src1 << BITS(src2, 5, 0));

  INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb     , I, R(dest) = SEXT(Mr(src1 + src2, 1), 8));
  INSTPAT("0000001 ????? ????? 101 ????? 01110 11", divuw  , R, R(dest) = SEXT(BITS(src1, 31, 0) / BITS(src2, 31, 0), 32));
  INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori    , I, R(dest) = src1 | src2);

  INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu   , R, R(dest) = src1 % src2);
  INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem    , R, R(dest) = (int64_t)src1 % (int64_t)src2);
  INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl    , R, R(dest) = src1 >> BITS(src2, 5, 0));
 
  INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div    , R, R(dest) = (int64_t)src1 / (int64_t)src2);
  INSTPAT("??????? ????? ????? 110 ????? 00000 11", lwu    , I, R(dest) = Mr(src1 +src2, 4));
  INSTPAT("??????? ????? ????? 001 ????? 11100 11", csrrw  , I, csr = RCSR(BITS(src2, 11, 0)); WCSR(BITS(src2, 11, 0), src1); R(dest) = csr);

  INSTPAT("??????? ????? ????? 010 ????? 11100 11", csrrs  , I, csr = RCSR(BITS(src2, 11, 0)); WCSR(BITS(src2, 11, 0), src1 | csr); R(dest) = csr);
  INSTPAT("0000000 00000 00000 000 00000 11100 11", ecall  , I, s->dnpc = isa_raise_intr(11, s->pc));
  INSTPAT("0011000 00010 00000 000 00000 11100 11", mret   , R, s->dnpc = cpu.mepc);
  INSTPAT("0000000 00000 00000 001 00000 00011 11", fence.i, I, );

  INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv    , N, INV(s->pc));
  INSTPAT_END();             

  R(0) = 0; // reset $zero to 0

  return 0;
}

int isa_exec_once(Decode *s) {
  s->isa.inst.val = inst_fetch(&s->snpc, 4);
  return decode_exec(s);
}
